#include "trx.h"

// *********** Global variables ***********
static trx_handle_t *trx_handle = NULL;

extern int errno;

#define ENABLE_TX 1

#define ENABLE_RX 1

#define ENABLE_ENV_UPDATE 1

//************************************************************************
//                   Program arguments processing
// ***********************************************************************
void trx_args_default(transceiver_args_t *args) {
  args->logging_frequency = 30000; // how frequent the log happens in milliseconds.
  args->single_log_duration = 1000; // duration of a single log in milliseconds.
  args->rnti = 0x1234;
  args->path_to_log_files = "./"; // location of the directory to collect log
  args->enable_cfo_correction = true; // By default CFO correction is enabled.
  args->nof_prb = 25;
  args->nof_ports = 1;
  args->rf_args = "";
  args->competition_center_frequency = 2400000000.0; // By default, if no center frequency is set, the center frequency will be 2.4 GHz.
  args->initial_rx_gain = 70.0; // AGC is disabled by default and the initial gain given to the RX chain is 70 dB. It will set the maximum gain in x310.
  args->initial_tx_gain = 70.0;
  args->path_to_start_file = "./start_iq_sample_capture.txt", // full path to the file generated by start.sh
  args->node_operation = -1; // 0 - RX, 1 - TX, -1 - Automatic configuration via basic control.
  args->node_id = 0; // SRN node ID. Each node can have a number spanning from 0 up to 255.
  args->intf_id = 0; // SRN RF Interface ID.
  args->max_number_of_dumps = 1, // Maxium number of dump files to be created.
  args->sensing_rx_gain = 0; // RX gain that should be applied to the specific channel RX cahin. Value given in dB.
  args->radio_id = 0; // By default all radio IDs are zero, meaning the Cell-ID is 0.
  args->use_std_carrier_sep = true; // By default, we use LTE standard sampling rates according to the the FFT size.
  args->initial_agc_gain = 50.0;
  args->competition_bw = 20000000.0; // Competition bandwidth is by default set to 20 MHz.
  args->default_tx_channel = 0;
  args->default_rx_channel = 0;
  args->lbt_threshold = 1000.0; // By default we set the channel threshold to 1000 dBm. In other others, LBT is disabled when threshold is too high.
  args->lbt_timeout = 10000000000000; // This parameter is given in milliseconds.
  args->max_turbo_decoder_noi = 15; // By default the maximum number of Turbo decoding iterations is set to 15.
  args->max_turbo_decoder_noi_for_high_mcs = 20; // By default the maximum number of turbo iterations for higher MCS values is set to 20.
  args->phy_filtering = false; // By default PHY filtering is disabled.
  args->iq_dump_data_type = SRSLTE_COMPLEX_SHORT_BIN; // By default we write complex<short> into file in order to get smaller files.
  args->rf_monitor_rx_sample_rate = 23040000.0; // By default the RF Monitor module will sample at 23.04 MHz.
  args->rf_monitor_channel = RF_MONITOR_CHANNEL; // By default we set the rf_monitor channel to 1.
  args->lbt_use_fft_based_pwr = false; // By default we use time-domain based power measurements.
  args->send_tx_stats_to_mac = false; // By default we do not send TX statistics to MAC as it does not use it for now.
  args->max_backoff_period = 32; // By default the maximum backoff period is set to 15.
  args->iq_dumping = false; // By default we never create IQ dumping files.
  args->immediate_transmission = false; // By default immediate transmissions are not allowed.
  args->add_tx_timestamp = false;
  args->rf_monitor_option = 100; // By default no rf monitor modules is set as the rf monitor option.
  args->initial_subframe_index = 5; // By default subframe is 5. Set the subframe index number to be used to start from.
  args->rf_monitor_fft_size = 512;
  args->rf_monitor_avg_size = 32;
  args->plot_rx_info = false; // By default plotting is disabled.
  args->rf_amp = 0.8; // RF Amp.
  args->trx_filter_idx = 0; // By default TRX filtering is disabled.
  args->enable_rfnoc_tx_fir = false; // By default FPGA TX FIR filter is not used.
  args->nof_phys = 2; // By default we use only two PHY as the number of radios is equal to 2 as well.
  args->default_phy_id = 0; // By default, when nof_phys is equal to 1 we use the RADIO 0.
  strcpy(args->env_pathname, DEFAULT_ENV_PATHNAME); // By default we point to the /root/radio_api folder.
  args->decode_pdcch = false; // By default we always try to decode SCH sequences instead of PDCCH control channel.
  args->enable_avg_psr = false; // By default we do not average PSR min/max values.
  args->threshold = 3.0; // PSS detection threshold.
  args->use_scatter_sync_seq = true; // Use customized Scatter PSS sequences.
  args->pss_len = 62; // length of the Scatter PSS sequence.
  args->pss_boost_factor = 1.0; // PSS boost factor, which is used to improve PSS detection with higher PSS power.
  args->enable_second_stage_pss_detection = false; // Enable or disable second stage PSS detection.
  args->pss_first_stage_threshold = 2.0; // Threshold of the first stage in the two-stage PSS detection mechanism.
  args->pss_second_stage_threshold = 3.5; // Threshold of the second stage in the two-stage PSS detection mechanism.
  args->enable_eob_pss = true; // Enable/Disable End of Busrt PSS.
}

void trx_usage(transceiver_args_t *args, char *prog) {
  printf("Usage: %s [abcdegiplomnsxqzwEXBPCRdDvrfUSZTIFLMNGQYWhV]\n", prog);
  printf("\t-a RF args [Default %s]\n", args->rf_args);
  printf("\t-b RF amp. [Default %s]\n", args->rf_amp);
  printf("\t-B Set competition bandwidth [Default %1.2f MHz]\n", args->competition_bw/1000000.0);
  printf("\t-g Set RX gain [Default %.1f dB]\n", args->initial_rx_gain);
  printf("\t-t Set TX gain [Default %.1f dB]\n", args->initial_tx_gain);
  printf("\t-l Set LBT threshold. Values greater than or equal to 100 dB disable LBT. [Default %1.0f dB]\n", args->lbt_threshold);
  printf("\t-o Set LBT timeout [Default %d milliseconds]\n", args->lbt_timeout);
  printf("\t-q Enable IQ dumping. Options are on/off [Default %s]\n", args->iq_dumping?"on":"off");
  printf("\t-c Use FFT based power measurements [Default %s]\n", args->lbt_use_fft_based_pwr?"Enabled":"Disabled");
  printf("\t-x Enable transmission of TX statistics to MAC layer [Default %s]\n", args->send_tx_stats_to_mac?"True":"False");
  printf("\t-X Set maximum backoff period [Default %d]\n", args->max_backoff_period);
  printf("\t-i Radio ID [Default: %d]\n",args->radio_id);
  printf("\t-n PHY Filtering [Default %s]\n", args->phy_filtering?"Enabled":"Disabled");
  printf("\t-N Add timestamp to TX packet [Default %d]\n", args->add_tx_timestamp);
  printf("\t-f Competition Center Frequency at which the PHY and the sensing modules should operate on (in Hz) [Default %.1f Hz]\n",args->competition_center_frequency);
  printf("\t-p Set number of Physical Resource Blocks (nof_prb) [Default %d]\n", args->nof_prb);
  printf("\t-P Set number of ports (nof_ports) [Default %d]\n", args->nof_ports);
  printf("\t-Q Enable TRX filtering [Default %d]\n", args->trx_filter_idx);
  printf("\t-r RNTI in Hex [Default 0x%x]\n",args->rnti);
  printf("\t-C Enable CFO correction [Default %s]\n", args->enable_cfo_correction?"Enabled":"Disabled");
  printf("\t-s RX sampling rate for RF monitor module [Default %1.2f]\n", args->rf_monitor_rx_sample_rate);
  printf("\t-m Maximum number of turbo decoder iterations [Default %d]\n", args->max_turbo_decoder_noi);
  printf("\t-S Path to check for start dumping file [Default %s]\n", args->path_to_start_file);
  printf("\t-L Path where the IQ dump files should be stored [Default %s]\n", args->path_to_log_files);
  printf("\t-I Standard Radio Node (SRN) ID [Default %d]\n", args->node_id);
  printf("\t-F Logging frequency in milliseconds, frequency in milliseconds with which dump files should be created [Default %d]\n", args->logging_frequency);
  printf("\t-D Single log file duration in milliseconds [Default %d]\n", args->single_log_duration);
  printf("\t-M Maximum number of dumping files to be created [Default %d]\n", args->max_number_of_dumps);
  printf("\t-G Gain given to the sensing RX chain [Default %1.2f]\n", args->sensing_rx_gain);
  printf("\t-d [set development debug level, default disabled]\n");
  printf("\t-v [set scatter_verbose_level to debug, default none]\n");
  printf("\t-Z Use non-standard sampling rates, [Default %d]\n",args->use_std_carrier_sep);
  printf("\t-T [set single operation mode of the node, i.e., it can operate only as TX or RX. 1 -> RX, 2 -> TX]\n");
  printf("\t-R Set the RF monitor module to be started. [Default %d]\n", args->rf_monitor_option);
  printf("\t-A Set the RF monitor FFT size. [Default %d]\n", args->rf_monitor_fft_size);
  printf("\t-V Set the RF monitor averaging size. [Default %d]\n", args->rf_monitor_avg_size);
  printf("\t-e Enable RFNoC trasmit FIR filter. [Default %s]\n", args->enable_rfnoc_tx_fir?"TRUE":"FALSE");
  printf("\t-w Set the subframe index to be used to start from. [Default %d]\n", args->initial_subframe_index);
  printf("\t-W Enable RX info plot. [Default %s]\n", args->plot_rx_info?"TRUE":"FALSE");
  printf("\t-E Set number of PHYs. [Default %d]\n", args->nof_phys);
  printf("\t-Y Default radio when nof_phys = 1. [Default %d]\n", args->default_phy_id);
  printf("\t-z Set environment pathname. [Default %s]\n", args->env_pathname);
  printf("\t-h Print this help message\n");
}

void trx_parse_args(transceiver_args_t *args, int argc, char **argv) {
  int opt;
  trx_args_default(args);
  while((opt = getopt(argc, argv, "abcdeogiplmnsxqzwEXBPQOCDvrfUSZTIFLMNGRAVYWht0123456789")) != -1) {
    switch (opt) {
    case 'i':
      args->radio_id = atoi(argv[optind]);
      break;
    case 'p':
      args->nof_prb = helpers_get_prb_from_bw(atoi(argv[optind]));
      TRX_PRINT("Requested PHY BW: %d - Mapped PHY PRB: %d\n",atoi(argv[optind]), args->nof_prb);
      break;
    case 'n':
      args->phy_filtering = !args->phy_filtering;
      TRX_PRINT("PHY filtering is %s.\n", args->phy_filtering?"Enabled":"Disabled");
      break;
    case 'N':
      args->add_tx_timestamp = true;
      TRX_PRINT("Addition of TX timestamp is enabled.\n",0);
      break;
    case 'P':
      args->nof_ports = atoi(argv[optind]);
      break;
    case 'a':
      TRX_PRINT("RF args: %s\n",argv[optind]);
      args->rf_args = argv[optind];
      break;
    case 'l':
      args->lbt_threshold = atof(argv[optind]);
      TRX_PRINT("LBT threshold: %1.2f\n",args->lbt_threshold);
      TRX_PRINT("LBT checking is %s\n",args->lbt_threshold < 100.0?"Enabled":"Disabled");
      break;
    case 'o':
      args->lbt_timeout = atoll(argv[optind]);
      TRX_PRINT("LBT timeout: %" PRIu64 "\n",args->lbt_timeout);
      break;
    case 'E':
      args->nof_phys = atoi(argv[optind]);
      if(args->nof_phys == 0 || args->nof_phys > MAX_NUM_CONCURRENT_PHYS) {
        TRX_ERROR("Number of PHYs must be greater than 0 and less than or equal to %d. Value set: %d\n", MAX_NUM_CONCURRENT_PHYS, args->nof_phys);
        exit(-1);
      }
      TRX_PRINT("Number of PHYs: %d\n", args->nof_phys);
      break;
    case 'z':
      strcpy(args->env_pathname, argv[optind]);
      TRX_PRINT("Environment pathname: %s\n", args->env_pathname);
      break;
    case 'q':
      if(strcmp("on", argv[optind]) == 0) {
        args->iq_dumping = true;
        // If IQ dumping is on then the RF monitor option must be 0, meaning, IQ Dump module.
        args->rf_monitor_option = 4;
      } else if(strcmp("off", argv[optind]) == 0) {
        args->iq_dumping = false;
      } else {
        args->iq_dumping = false;
      }
      TRX_PRINT("IQ dumping: %s\n",args->iq_dumping?"on":"off");
      break;
    case 'R':
      if(strcmp("iq_dumping", argv[optind]) == 0) {
        args->rf_monitor_option = 0;
      } else if(strcmp("lbt", argv[optind]) == 0) {
        args->rf_monitor_option = 1;
      } else if(strcmp("rssi_monitor", argv[optind]) == 0) {
        args->rf_monitor_option = 2;
      } else if(strcmp("spectrum_sensing", argv[optind]) == 0) {
        args->rf_monitor_option = 3;
      } else if(strcmp("iq_dump_plus_sensing", argv[optind]) == 0) {
        args->rf_monitor_option = 4;
      } else if(strcmp("rfnoc_sensing", argv[optind]) == 0) {
        args->rf_monitor_option = 5;
      } else {
        args->rf_monitor_option = 100;
      }
      TRX_PRINT("RF monitor option: %d\n",args->rf_monitor_option);
      break;
    case 'A':
      args->rf_monitor_fft_size = atoi(argv[optind]);
      TRX_PRINT("RF monitor FFT size: %d\n",args->rf_monitor_fft_size);
      break;
    case 'V':
      args->rf_monitor_avg_size = atoi(argv[optind]);
      TRX_PRINT("RF monitor averaging size: %d\n",args->rf_monitor_avg_size);
      break;
    case 'e':
      args->enable_rfnoc_tx_fir = true;
      TRX_PRINT("FPGA transmit filter enabled \n", 0);
      break;
    case 'Y':
      args->default_phy_id = atoi(argv[optind]);
      if(args->default_phy_id > 1) {
        TRX_ERROR("Default PHY ID has to be lesser than or equal to 1. Value set: %d\n", args->default_phy_id);
        exit(-1);
      }
      TRX_PRINT("Default radio when nof_phys = 1: %d\n",args->default_phy_id);
      break;
    case 'B':
      args->competition_bw = atof(argv[optind]);
      TRX_PRINT("Competition BW: %.2f [MHz]\n",args->competition_bw/1000000.0);
      break;
    case 'b':
      args->rf_amp = atof(argv[optind]);
      TRX_PRINT("RF amp: %f\n",args->rf_amp);
      break;
    case 'g':
      args->initial_rx_gain = atof(argv[optind]);
      break;
    case 't':
      args->initial_tx_gain = atof(argv[optind]);
      break;
    case 'c':
      args->lbt_use_fft_based_pwr = true;
      TRX_PRINT("FFT based power measurement is enabled.\n",0);
      break;
    case 'W':
#if(ENBALE_RX_INFO_PLOT==1)
      args->plot_rx_info = true;
      TRX_PRINT("Plot of RX info enabled.\n",0);
#else
      TRX_PRINT("Plot of RX info needs to be enabled and compiled.\n",0);
#endif
      break;
    case 'w':
      args->initial_subframe_index = atoi(argv[optind]);
      TRX_PRINT("Initial subframe index set to: %d\n",args->initial_subframe_index);
      break;
    case 'x':
      args->send_tx_stats_to_mac = true;
      TRX_PRINT("Transmission of PHY TX stats to MAC layer is enabled.\n",0);
      break;
    case 'X':
      args->max_backoff_period = atoi(argv[optind]);
      TRX_PRINT("Maximum backoff period set to: %d\n",args->max_backoff_period);
      break;
    case 'C':
      args->enable_cfo_correction = false;
      TRX_PRINT("CFO correction disabled\n",0);
      break;
    case 'f':
      args->competition_center_frequency = strtod(argv[optind], NULL);
      TRX_PRINT("Center frequency received from command line: %1.3f [MHz]\n",args->competition_center_frequency/1000000.0);
      break;
    case 'r':
      args->rnti = strtol(argv[optind], NULL, 16);
      break;
    case 'd':
      development_debug_level++;
      break;
    case 'v':
      scatter_verbose_level++;
      break;
    case 'Q':
#if(ENABLE_PHY_TX_FILTERING==1)
      args->trx_filter_idx++;
      if(args->trx_filter_idx==1) {
        TRX_PRINT("TRX Filtering enabled.\n",0);
      }
#else
      TRX_ERROR("SW filter is disabled by default!!!\n",0);
      TRX_ERROR("If you want to enable it run: \"cmake -DENABLE_PHY_TX_FILTERING=ON ../\" and recompile again.\n",0);
      exit(-1);
#endif
      break;
    case 'm':
      args->max_turbo_decoder_noi = atoi(argv[optind]);
      TRX_PRINT("Max. turbo decoding iterations: %d\n",args->max_turbo_decoder_noi);
      break;
    case 's':
      args->rf_monitor_rx_sample_rate = atof(argv[optind]);
      TRX_PRINT("RF Monitor sampling rate: %1.2f [MHz]\n",args->rf_monitor_rx_sample_rate/1000000.0);
      break;
    case 'Z':
      args->use_std_carrier_sep = false;
      TRX_PRINT("Using Non-standard sampling rates.\n",0);
      break;
    case 'T':
      args->node_operation = atoi(argv[optind]);
      TRX_PRINT("Node Operation: %d\n",args->node_operation);
      break;
    case 'S':
      args->path_to_start_file = argv[optind];
      break;
    case 'D':
      args->single_log_duration = atoi(argv[optind]);
      break;
    case 'L':
      args->path_to_log_files = argv[optind];
      break;
    case 'I':
      args->node_id = atoi(argv[optind]);
      if(args->node_id >= MAXIMUM_NUMBER_OF_RADIOS) {
        TRX_ERROR("Invalid Node ID: %d. It has to be less than %d.\n", args->node_id, MAXIMUM_NUMBER_OF_RADIOS);
        exit(-1);
      }
      TRX_PRINT("SRN ID: %d\n",args->node_id);
      break;
    case 'F':
      args->logging_frequency = atoi(argv[optind]);
      break;
    case 'M':
      args->max_number_of_dumps = atoi(argv[optind]);
      break;
    case 'G':
      args->sensing_rx_gain = atof(argv[optind]);
      break;
    case '0':
    case '1':
    case '2':
    case '3':
    case '4':
    case '5':
    case '6':
    case '7':
    case '8':
    case '9':
      break;
    case 'h':
    default:
      trx_usage(args, argv[0]);
      exit(-1);
    }
  }
  char fft_size_string[100];
  if(strcmp(args->rf_args, "")) {
    sprintf(fft_size_string, ",fft_size=%d", args->rf_monitor_fft_size);
    strcat(args->rf_args, fft_size_string);
  } else {
    sprintf(fft_size_string, "fft_size=%d", args->rf_monitor_fft_size);
    args->rf_args = fft_size_string;
  }
  if(args->enable_rfnoc_tx_fir){ // Append enable_fir to rf_args, to be used by uhd_usrp_make
      strcat(args->rf_args, ",enable_fir");
    TRX_PRINT("RF args: %s\n",args->rf_args);
  }
}
/**********************************************************************/

void trx_sig_int_handler(int signo) {
  if(signo == SIGINT) {
    trx_handle->go_exit = true;
    TRX_PRINT("SIGINT received. Exiting...\n",0);
  }
}

void trx_initialize_signal_handler() {
  sigset_t sigset;
  sigemptyset(&sigset);
  sigaddset(&sigset, SIGINT);
  sigprocmask(SIG_UNBLOCK, &sigset, NULL);
  signal(SIGINT, trx_sig_int_handler);
}

void trx_open_rf_device() {
  // Open USRP.
  TRX_PRINT("Opening RF device...\n",0);
  if(srslte_rf_open(&trx_handle->rf, trx_handle->prog_args.rf_args)) {
    TRX_ERROR("Error opening rf.\n",0);
    exit(-1);
  }
}

void trx_close_rf_device() {
  if(srslte_rf_close(&trx_handle->rf)) {
    TRX_ERROR("Error closing rf.\n",0);
    exit(-1);
  }
  TRX_PRINT("srslte_rf_close done!\n",0);
}

void trx_change_process_priority(int inc) {
  errno = 0;
  if(nice(inc) == -1) {
    if(errno != 0) {
      TRX_ERROR("Something went wrong with nice(): %s - Perhaps you should run it as root.\n",strerror(errno));
    }
  }
}

void trx_set_master_clock_rate() {
  // We always set the master clock rate to the maximum one, i.e., for a system with 100 PRB (20 MHz BW).
  uint32_t nof_prb = 100;
  // Calculate the master clock rate for a bandwidth of 20 MHz, i.e., 100 PRB.
  int srate = srslte_sampling_freq_hz(nof_prb);
  // Set the master clock rate.
  srslte_rf_set_master_clock_rate(&trx_handle->rf, srate);
  TRX_PRINT("Set master clock rate to: %.2f [MHz]\n", (float)srate/1000000);
}

#if(ENABLE_SENSING_THREAD==1)
void trx_initialize_rf_monitor() {
  if(trx_handle->prog_args.rf_monitor_option <= 4 && trx_handle->rf.num_of_channels > 1) {
    int rc = rf_monitor_initialize(&trx_handle->rf, &trx_handle->prog_args);
    if(rc) {
      TRX_ERROR("ABORTING; It was not possible to start the sensing module.\n",0);
      exit(-1);
    }
  }else if(trx_handle->prog_args.rf_monitor_option == 5) {
    srslte_rf_rf_monitor_initialize(&trx_handle->rf, trx_handle->prog_args.competition_center_frequency, trx_handle->prog_args.rf_monitor_rx_sample_rate, PHY_RX_LO_OFFSET, trx_handle->prog_args.rf_monitor_fft_size, trx_handle->prog_args.rf_monitor_avg_size);
  }
}

void trx_uninitialize_rf_monitor() {
   if(trx_handle->prog_args.rf_monitor_option <= 4  && trx_handle->rf.num_of_channels > 1) {
     int rc = rf_monitor_uninitialize();
     if(rc) {
       TRX_ERROR("ABORTING; It was not possible to uninitialize the sensing module.\n",0);
       exit(-1);
     }
     TRX_PRINT("RF monitoring uninitialization done!\n",0);
   } else if(trx_handle->prog_args.rf_monitor_option == 5) {
     srslte_rf_rf_monitor_uninitialize(&trx_handle->rf);
   }
}
#endif

void trx_get_module_and_target_name(char *module_name, char *target1_name, char *target2_name) {
  // If we are testing the PHY with a single host PC we need to have two distinct names for PHY, otherwise, the test scripts will receive
  // responses from the other PHY.
  if(trx_handle->prog_args.node_operation == -1) {
    strcpy(module_name, "MODULE_PHY");
    strcpy(target1_name, "MODULE_MAC");
    strcpy(target2_name, "MODULE_ENVIRONMENT");
    TRX_DEBUG("module_name: %s - target1_name: %s\n",module_name, target1_name);
  } else if(trx_handle->prog_args.node_operation == PHY_DEBUG_1) {
    strcpy(module_name, "MODULE_PHY_DEBUG_1");
    strcpy(target1_name, "MODULE_MAC_DEBUG_1");
    target2_name = NULL;
    TRX_DEBUG("module_name: %s - target1_name: %s\n",module_name, target1_name);
  } else if(trx_handle->prog_args.node_operation == PHY_DEBUG_2) {
    strcpy(module_name, "MODULE_PHY_DEBUG_2");
    strcpy(target1_name, "MODULE_MAC_DEBUG_2");
    target2_name = NULL;
    TRX_DEBUG("module_name: %s - target1_name: %s\n",module_name, target1_name);
  } else {
    TRX_ERROR("Node operation was not defined correctly.\n",0);
    exit(-1);
  }
}

//*************************************************************************
//                            BEGIN OF MAIN
//*************************************************************************
int main(int argc, char **argv) {

  LayerCommunicator_handle handle = NULL;
  char module_name[100], target1_name[100], target2_name[100];
  bool ret;
  basic_ctrl_t basic_ctrl;
#if(ENABLE_ENV_UPDATE==1)
  environment_t env_update;
#endif

  // Set priority to main thread.
  uhd_set_thread_priority(1.0, true);

  // Allocate memory for a new trx object.
  trx_handle = (trx_handle_t*)srslte_vec_malloc(sizeof(trx_handle_t));
  // Check if memory allocation was correctly done.
  if(trx_handle == NULL) {
    TRX_ERROR("Error allocating memory for TRX context.\n",0);
    return -1;
  }
  // Initialize flag to exit phy.
  trx_handle->go_exit = false;
  // Change process priority.
  trx_change_process_priority(-20);
  // Initialize signal handler.
  trx_initialize_signal_handler();
  // Register watchdog callback
  if(timer_register_callback(trx_watchdog_handler) < 0) {
    TRX_ERROR("Not possible to register watchdog callback.\n",0);
    exit(-1);
  }
  // Parse command line arguments.
  trx_parse_args(&trx_handle->prog_args, argc, argv);
  // Open RF device (USRP).
  trx_open_rf_device();
  // Set master clock rate.
  // We always set the master clock rate to the maximum one, i.e., for a system with 100 PRB (20 MHz BW).
  trx_set_master_clock_rate();
  // Set FPGA time to the current host time so that we can use the host time (ntp) to synchronize among nodes.
  trx_set_fpga_time(&trx_handle->rf);
  // Rertieve target name.
  trx_get_module_and_target_name(module_name, target1_name, target2_name);
  // Instantiate communicator module so that we can receive/transmit commands and data
  communicator_initialization(module_name, target1_name, target2_name, &handle, MAX_NUM_CONCURRENT_PHYS, trx_handle->prog_args.env_pathname);
  // Verify if enviroment update file exists and if the parameters are different from default ones.
  trx_verify_environment_update_file_existence(&trx_handle->prog_args);

#if(ENABLE_RX==1)
  // Initialize PHY reception thread.
  for(int phy_id = 0; phy_id < trx_handle->prog_args.nof_phys; phy_id++) {
    if(phy_reception_start_thread(handle, &trx_handle->rf, &trx_handle->prog_args, (trx_handle->prog_args.default_phy_id + phy_id)) < 0) {
      TRX_ERROR("PHY ID: %d - It was not possible to start the PHY reception thread.\n", (trx_handle->prog_args.default_phy_id + phy_id));
      exit(-1);
    }
    TRX_PRINT("PHY ID: %d - PHY reception thread initialized.\n", phy_id);
    // Keep reception timer id's address for checking.
    trx_handle->rx_timer_ids[phy_id] = phy_reception_get_timer_id(phy_id);
  }
  TRX_PRINT("All Rx threads were successfully started.\n", 0);
#endif

#if(ENABLE_TX==1)
  for(int phy_id = 0; phy_id < trx_handle->prog_args.nof_phys; phy_id++) {
    // Initialize PHY transmission thread.
    if(phy_transmission_start_thread(handle, &trx_handle->rf, &trx_handle->prog_args, (trx_handle->prog_args.default_phy_id + phy_id)) < 0) {
      TRX_ERROR("PHY ID: %d - It was not possible to start the PHY transmission thread.\n", (trx_handle->prog_args.default_phy_id + phy_id));
      exit(-1);
    }
    TRX_PRINT("PHY ID: %d - PHY transmission thread initialized.\n", phy_id);
    // Keep transmission timer id's address for checking.
    trx_handle->tx_timer_ids[phy_id] = phy_transmission_get_timer_id(phy_id);
  }
  TRX_PRINT("All Tx threads were successfully started.\n", 0);
#endif

#if(ENABLE_SENSING_THREAD==1)
  // Initialize rf monitor: After setting the master clock rate we can start the rf monitor thread only if the number of opened channels is greater than 1.
  TRX_PRINT("Initializing RF Monitor.\n",0);
  trx_initialize_rf_monitor();
  TRX_PRINT("RF Monitor initialized.\n",0);
#endif

#if(ENABLE_CH_EMULATOR_IMPAIRMENTS==1)
  #ifdef ENABLE_CH_EMULATOR
  // Enable simple AWGN channel.
  rf_channel_emulator_set_simple_awgn_channel(&trx_handle->rf, true);
  // Set noise variance of the AWGN channel.
  float snr = -3.0;//23.0;
  rf_channel_emulator_add_simple_awgn(&trx_handle->rf, snr);
  #endif
#endif

  TRX_PRINT("-------------------------------------------------\n\n",0);

  //**************************************** Handle incoming messages - BEGIN *****************************************
  // Loop where the FSM is implemented.
  while(!trx_handle->go_exit) {

#if(ENBALE_TX_PROFILLING==1)
    // Measure the time it takes for environment update.
    uint64_t env_update_timestamp_start = helpers_get_host_time_now();
#endif

#if(ENABLE_ENV_UPDATE==1)
    // Check for environment update messages.
    ret = communicator_get_high_queue(handle, (void * const)&env_update, NULL);
    if(!trx_handle->go_exit && ret) {
      trx_handle_update_env_messages(&env_update);
    }
#endif

#if(ENBALE_TX_PROFILLING==1)
    uint64_t env_update_timestamp_end = helpers_get_host_time_now();
    int envu_tdif = (int)(env_update_timestamp_end - env_update_timestamp_start);
    if(envu_tdif > 1000) {
      TRX_ERROR("Env. update time diff: %d - ret: %d\n", envu_tdif, ret);
    }
    // Measure the time it takes for environment update.
    uint64_t bc_timestamp_start = helpers_get_host_time_now();
#endif

    // Try to retrieve a message from the QUEUE. It waits for a specified amount of time before timing out.
    ret = communicator_get_low_queue_wait_for(handle, 500, (void * const)&basic_ctrl, NULL);
    // If message is properly retrieved and parsed, then relay it to the correct module.
    if(!trx_handle->go_exit && ret) {
      // Call FSM function to handle the state.
      trx_handle_mac_messages(&basic_ctrl);
    }

#if(ENBALE_TX_PROFILLING==1)
    uint64_t bc_timestamp_end = helpers_get_host_time_now();
    int bc_tdif = (int)(bc_timestamp_end - bc_timestamp_start);
    if(bc_tdif > 1000) {
      TRX_ERROR("Basic control time diff: %d - ret: %d\n", bc_tdif, ret);
    }
#endif

  }
  //**************************************** Handle incoming messages - END *****************************************
  TRX_PRINT("Start uninitialization of modules.\n",0);

  // After use, communicator handle MUST be freed.
  communicator_uninitialization(&handle);
  TRX_PRINT("Communicator handle freed.\n",0);

#if(ENABLE_SENSING_THREAD==1)
  // Free all resources used by the sensing module.
  TRX_PRINT("Unitializing RF Monitor.\n",0);
  trx_uninitialize_rf_monitor();
  TRX_PRINT("RF Monitor unitialized.\n",0);
#endif

#if(ENABLE_TX==1)
  for(int phy_id = 0; phy_id < trx_handle->prog_args.nof_phys; phy_id++) {
    // Stop and free all resources related to the PHY transmission thread.
    if(phy_transmission_stop_thread((trx_handle->prog_args.default_phy_id + phy_id)) < 0) {
      TRX_ERROR("PHY ID: %d - It was not possible to uninitialize the PHY transmission thread.\n", (trx_handle->prog_args.default_phy_id + phy_id));
      exit(-1);
    }
    TRX_PRINT("PHY ID: %d - Transmission uninitialization done!\n", (trx_handle->prog_args.default_phy_id + phy_id));
  }
#endif

#if(ENABLE_RX==1)
  for(int phy_id = 0; phy_id < trx_handle->prog_args.nof_phys; phy_id++) {
#ifdef ENABLE_CH_EMULATOR
    // Inform channel emulator that is has to stop running.
    rf_channel_emulator_stop_running(&trx_handle->rf, phy_id);
#endif // end of ENABLE_CH_EMULATOR
    // Stop and free all resources related to the PHY reception thread.
    if(phy_reception_stop_thread((trx_handle->prog_args.default_phy_id + phy_id)) < 0) {
      TRX_ERROR("PHY ID: %d - It was not possible to uninitialize the PHY reception thread.\n", (trx_handle->prog_args.default_phy_id + phy_id));
      exit(-1);
    }
    TRX_PRINT("PHY ID: %d - Reception uninitialization done!\n", (trx_handle->prog_args.default_phy_id + phy_id));
  }
#endif

  // Close RF device.
  trx_close_rf_device();

  // Free memory used to store Rx object.
  if(trx_handle) {
    free(trx_handle);
    trx_handle = NULL;
  }
  TRX_PRINT("TRX handle uninitialization done!\n",0);

  TRX_PRINT("Bye...\n",0);
  exit(0);
}

int trx_handle_mac_messages(basic_ctrl_t *basic_ctrl) {
  // Validate PHY ID.
  if(trx_handle->prog_args.nof_phys < MAX_NUM_CONCURRENT_PHYS) {
    if(basic_ctrl->phy_id != trx_handle->prog_args.default_phy_id) {
      TRX_ERROR("PHY ID in MAC basic control: %d does not match default PHY ID: %d - Dropping this control message....\n", basic_ctrl->phy_id, trx_handle->prog_args.default_phy_id);
      return -1;
    }
  }
  switch(basic_ctrl->trx_flag) {
    case PHY_TX_ST:
    {
#if(ADJUST_FPGA_TIME==1)
      // Adjust FPGA time.
      trx_adjust_fpga_time(&trx_handle->rf);
#endif
#if(ENABLE_TX==1)
  #if(ENBALE_TX_PROFILLING==1)
      // Measure the time it takes for a basic control message to go from the main to Tx thread. The timestamp is read in phy_transmission.c.
      basic_ctrl->debugging_timestamp_start = helpers_get_host_time_now();
      int tdiff = (int)(basic_ctrl->timestamp - basic_ctrl->debugging_timestamp_start);
      if(tdiff < 1500) {
        TRX_ERROR("0MQ time diff at TRX: %d\n", tdiff);
      }
  #endif
      // Push basic Tx control into circular buffer.
      phy_transmission_push_tx_basic_control_into_container(basic_ctrl);
#endif
      break;
    }
    case PHY_RX_ST:
    {
#if(ENABLE_RX==1)
      if(phy_reception_change_parameters(basic_ctrl) < 0) {
        TRX_ERROR("Error changing Rx parameters.\n",0);
      }
#endif
      break;
    }
    default:
      TRX_ERROR("Invalid state...\n",0);
  }
  // Everyhting went well.
  return 0;
}

#if(ENABLE_ENV_UPDATE==1)
void trx_handle_update_env_messages(environment_t *env_update) {
  // Check if environment has really changed.
  if(env_update->environment_updated) {
    TRX_PRINT("Environment update received, informing PHYs....\n", 0);
    // Update enviroment for all the running PHYs.
    for(int phy_id = 0; phy_id < trx_handle->prog_args.nof_phys; phy_id++) {
#if(ENABLE_RX==1)
      // Update transmission parameters.
      phy_reception_update_environment((trx_handle->prog_args.default_phy_id + phy_id), env_update);
#endif
#if(ENABLE_TX==1)
      // Update transmission parameters.
      phy_transmission_update_environment((trx_handle->prog_args.default_phy_id + phy_id), env_update);
#endif
    }
  } else {
    TRX_ERROR("Environment update received but flag was not true...\n", 0);
  }
}

void trx_verify_environment_update_file_existence(transceiver_args_t* args) {
  environment_t env;
  if(communicator_verify_environment_update(&env)) {
    if(args->competition_bw != env.competition_bw) {
      TRX_PRINT("Changing default competion BW from %1.2f [MHz] to %1.2f [MHz].\n", args->competition_bw/1e6, env.competition_bw/1e6);
      args->competition_bw = env.competition_bw;
    } else {
      TRX_PRINT("Competion BW in env. update file: %1.2f [MHz] is the same as the one at command line: %1.2f [MHz].\n", env.competition_bw/1e6, args->competition_bw/1e6);
    }
    if(args->competition_center_frequency != env.competition_center_freq) {
      TRX_PRINT("Changing default competion center freq. from %1.2f [MHz] to %1.2f [MHz].\n", args->competition_center_frequency/1e6, env.competition_center_freq/1e6);
      args->competition_center_frequency = env.competition_center_freq;
    } else {
      TRX_PRINT("Competion center freq. in env. update file: %1.2f [MHz] is the same as the one at command line: %1.2f [MHz].\n", env.competition_center_freq/1e6, args->competition_center_frequency/1e6);
    }
  } else {
    TRX_PRINT("Environment update file not present at phy start up....\n",0);
  }
}
#endif

static void trx_watchdog_handler(int sig, siginfo_t *info, void *ptr) {
  // Print a string that will be used by the standalone PHY watchdog.
  TRX_PRINT("PHY_Watchdog_Activated\n", 0);
  for(int phy_id = 0; phy_id < trx_handle->prog_args.nof_phys; phy_id++) {
     // Verify if one of the Rx threads actived the watchdog.
     if(info->si_value.sival_ptr == trx_handle->rx_timer_ids[phy_id]) {
       TRX_PRINT("Watchdog activated in PHY ID: %d Rx thread.\n", phy_id);
     }
     // Verify if one of the Tx threads actived the watchdog.
     if(info->si_value.sival_ptr == trx_handle->tx_timer_ids[phy_id]) {
       TRX_PRINT("Watchdog activated in PHY ID: %d Tx thread.\n", phy_id);
     }
  }
}
